package com.fzhucc.modules.security.service

import com.fzhucc.exception.BadRequestException
import com.fzhucc.modules.security.config.bean.LoginProperties
import com.fzhucc.modules.security.config.bean.SecurityProperties
import com.fzhucc.modules.security.domain.OnlineUser
import com.fzhucc.modules.security.domain.bo.WxAuthenticationToken
import com.fzhucc.modules.security.domain.dto.AuthDto
import com.fzhucc.modules.security.domain.dto.JwtUserDto
import com.fzhucc.modules.security.security.TokenProvider
import com.fzhucc.modules.system.domain.User
import com.fzhucc.modules.system.repository.RoleRepository
import com.fzhucc.modules.system.repository.UserRepository
import com.fzhucc.modules.system.service.UserService
import com.fzhucc.modules.system.service.impl.MsUserService
import com.fzhucc.ms.auth.domain.enums.AuthType
import com.fzhucc.utils.HttpUtils2
import com.fzhucc.utils.JwtSecurityUtils
import com.fzhucc.utils.WxClient
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import javax.annotation.Resource
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpUtils

/**
 * @author Woolsen
 */
@Service
class AuthenticationService(
    private val userRepository: UserRepository,
    private val roleRepository: RoleRepository,
    private val passwordEncoder: PasswordEncoder,
    private val userDetailService: UserDetailsServiceImpl,
    private val wxClient: WxClient,
    private val properties: SecurityProperties,
    private val onlineUserService: OnlineUserService,
    private val tokenProvider: TokenProvider,
    private val authenticationManagerBuilder: AuthenticationManagerBuilder,
    @Resource
    private val loginProperties: LoginProperties,
    private val msUserService: MsUserService,
) {

    companion object {
        //FIXME type和roleId耦合，type=roleId
        private val ALLOW_TYPES_IN_REGISTER = setOf(
            3,  //企业/团队
            4,  //专家
            5,  //智库
            6,  //投资机构
            7,  //技术经理人
            8,  //其他
        )

        private val log = LoggerFactory.getLogger(AuthenticationService::class.java)
    }

    fun login(username: String, password: String, request: HttpServletRequest): AuthDto {
        var jwtUserDto: JwtUserDto
        var token: String
        try {
            msUserService.login(username, password)
            token = tokenProvider.createToken(username)
            jwtUserDto = userDetailService.loadUserByUsername(username)
            val authentication = UsernamePasswordAuthenticationToken(jwtUserDto, "", jwtUserDto.authorities)
            SecurityContextHolder.getContext().authentication = authentication
        } catch (e: Exception) {
            log.error("认证微服务登录失败, ${e.message}")
            val authenticationToken = UsernamePasswordAuthenticationToken(username, password)
            val authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken)
            SecurityContextHolder.getContext().authentication = authentication
            token = tokenProvider.createToken(authentication)
            jwtUserDto = authentication.principal as JwtUserDto
        }

        val authDto = AuthDto(
            properties.tokenStartWith + token,
            jwtUserDto
        )

        // 保存在线信息
        onlineUserService.save(jwtUserDto, OnlineUser.SYSTEM, token, request)
        if (loginProperties.isSingleLogin) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(username, token)
        }
        return authDto
    }

    /**
     * @param type 若不为null，则当用户不存在时自动注册
     */
    fun authenticateByUnionId(unionId: String, type: Int?): WxAuthenticationToken {
        val jwtUserDto: JwtUserDto = try {
            val dto = msUserService.oauth(AuthType.WX_UNION_ID, unionId)
            userDetailService.loadUserByUsername(dto.username)
        } catch (e: Exception) {
            try {
                userDetailService.loadUserByUsername(unionId)
            } catch (e: UsernameNotFoundException) {
//                if (type == null) {
//                    throw e
//                }
                registerByUnionId(unionId, type)
                userDetailService.loadUserByUsername(unionId)
            }
        }
        return WxAuthenticationToken(jwtUserDto, jwtUserDto.authorities)
    }

    fun authenticateByUnionId(unionId: String): WxAuthenticationToken {
        return authenticateByUnionId(unionId, null)
    }

    fun authenticateByUnionIdPhone(phone: String): WxAuthenticationToken {
        return authenticateByUnionIdPhone(phone, null)
    }

    /**
     * @param type 若不为null，则当用户不存在时自动注册
     */
    fun authenticateByUnionIdPhone(phone: String, type: Int?): WxAuthenticationToken {
        val jwtUserDto: JwtUserDto = try {
            val dto = msUserService.oauth(AuthType.WX_UNION_ID, phone)
            userDetailService.loadUserByUsername(dto.username)
        } catch (e: Exception) {
            try {
                userDetailService.loadUserByUsername(phone)
            } catch (e: UsernameNotFoundException) {
//                if (type == null) {
//                    throw e
//                }
                registerByUnionIdPhone(phone, 3)
                userDetailService.loadUserByUsername(phone)
            }
        }
        return WxAuthenticationToken(jwtUserDto, jwtUserDto.authorities)
    }

    fun registerByUnionIdPhone(phone: String, type: Int?) {
        //TODO 直接注册账号，密码为手机号后6位并能登录 不用选地区市
        if (!ALLOW_TYPES_IN_REGISTER.contains(type)) {
            throw BadRequestException("非法用户类型")
        }
        var user = User()
        user.unionId = phone
        user.username = phone
        //user.nickName = "市总H5用户#${phone.hashCode().toString().take(4)}"
        user.nickName = phone
        user.phone = phone
        user.enabled = true
        val substring = phone.substring(phone.length - 6)
        user.password = passwordEncoder.encode(substring)
        //user.orgArea = "福建省;福州市;" //工会进来的就 默认设置福建省;福州市;
        user.h5area = "福建省;福州市;" //工会进来的就 默认设置福建省;福州市;
        user.isAdmin = false
        user.integral = 2000
        //FIXME type和roleId耦合
        if (type != null) {
            val role = roleRepository.findByIdOrNull(type.toLong())
            user.roles = if (role == null) emptySet() else setOf(role)
            user.type = type.toLong()
        }
        user = userRepository.save(user)

        try {
            // 注册到认证微服务
            val dto = msUserService.create(user)
            msUserService.bind(dto.id, AuthType.WX_UNION_ID, phone)
        } catch (e: Exception) {
        }
    }

    /**
     * 通过微信小程序code登录。若用户不存在，则自动注册
     */
    @Throws(UsernameNotFoundException::class)
    fun authenticateByWxMpCode(code: String, type: Int): WxAuthenticationToken {
        val unionId = wxClient.authMpCode(code)
        return authenticateByUnionId(unionId, type)
    }

    fun registerByUnionId(unionId: String, type: Int?) {
        if (!ALLOW_TYPES_IN_REGISTER.contains(type)) {
            throw BadRequestException("非法用户类型")
        }
        var user = User()
        user.unionId = unionId
        user.username = unionId
        user.nickName = null
        user.enabled = true
        //user.orgArea = "福建省;福州市;" //工会进来的就 默认设置福建省;福州市;
        user.h5area = "福建省;福州市;" //工会进来的就 默认设置福建省;福州市;
        user.isAdmin = false
        //FIXME type和roleId耦合
        if (type != null) {
            val role = roleRepository.findByIdOrNull(type.toLong())
            user.roles = if (role == null) emptySet() else setOf(role)
            user.type = type.toLong()
        }
        user = userRepository.save(user)

        try {
            // 注册到认证微服务
            val dto = msUserService.create(user)
            msUserService.bind(dto.id, AuthType.WX_UNION_ID, unionId)
        } catch (e: Exception) {
        }
    }

    fun loginTemporary(type: String, resp: HttpServletResponse) {
        if (type == null) {
            throw BadRequestException("非法类型")
        }
        if (!type.equals("zqx")) {
            throw BadRequestException("非法类型")
        }
        val currentUserId = JwtSecurityUtils.getCurrentUserId()
        val res = userRepository.findById(currentUserId)
        if (!res.isPresent) {
            throw BadRequestException("未找到登录用户")
        }
        val user = res.get()
        val phone = user.getPhone()
        if (phone == null || phone.equals("")) {
            throw BadRequestException("请先绑定手机号后操作")
        }
        if (type.equals("zqx")) {
            // 设置Access-Control-Allow-Origin头
            //resp.setHeader("Access-Control-Allow-Origin", "https://kccg.fzzgsc.com");
            resp.sendRedirect("https://zqx.fzzgsc.com/api/sys/temporaryLogin?phone=" + phone + "&type=" + type)
        }
    }


    @Transactional
    fun sciloginkc(phone: String, resp: HttpServletResponse, request: HttpServletRequest) {
        var userres = User()
        var list = userRepository.findAllsByPhone(phone)
        if (list!=null && list.size > 1){
            throw BadRequestException("存在多个相同手机号的用户，无法操作")
        }
        if (list == null || list.size == 0) {
            //注册账号
            val res = User()
            res.unionId = null
            res.username = phone
            res.nickName = phone
            res.phone = phone
            //res.orgArea = "福州市"
            res.enabled = true
            res.isAdmin = false
            val type = 3
            //FIXME type和roleId耦合
            if (type != null) {
                val role = roleRepository.findByIdOrNull(type.toLong())
                res.roles = if (role == null) emptySet() else setOf(role)
                res.type = type.toLong()
            }
            userres = userRepository.save(res)
        }
        userres = list.get(0)
        var jwtUserDto: JwtUserDto
        var token: String
        try {
            msUserService.login(userres.username, userres.password)
            token = tokenProvider.createToken(userres.username)
            jwtUserDto = userDetailService.loadUserByUsername(userres.username)
            val authentication = UsernamePasswordAuthenticationToken(jwtUserDto, "", jwtUserDto.authorities)
            SecurityContextHolder.getContext().authentication = authentication
        } catch (e: Exception) {
            log.error("认证微服务登录失败, ${e.message}")
            val authenticationToken = UsernamePasswordAuthenticationToken(userres.username, userres.password)
            val authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken)
            SecurityContextHolder.getContext().authentication = authentication
            token = tokenProvider.createToken(authentication)
            jwtUserDto = authentication.principal as JwtUserDto
        }

        val authDto = AuthDto(
            properties.tokenStartWith + token,
            jwtUserDto
        )

        // 保存在线信息
        onlineUserService.save(jwtUserDto, OnlineUser.SYSTEM, token, request)
        if (loginProperties.isSingleLogin) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(userres.username, token)
        }

        resp.sendRedirect("https://kccg.fzzgsc.com/dashboard?token=" + authDto.token)

    }
}